// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT license.

#ifndef STATE_MANAGER_H_
#define STATE_MANAGER_H_

#include <stdint.h>
#include <stdbool.h>
#include "platform.h"
#include "status/rot_status.h"
#include "flash/flash.h"


/**
 * Definitions to indicate which region of flash holds the active manifest.
 */
enum manifest_region {
	MANIFEST_REGION_1,			/**< The primary manifest region contains the active manifest. */
	MANIFEST_REGION_2,			/**< The secondary manifest region contains the active manifest. */
};


/**
 * Manager for state information.
 */
struct state_manager {
	struct flash *nv_store;		/**< The flash that contains the stored state. */
	uint32_t base_addr;			/**< The first address of the state storage. */
	uint32_t store_addr;		/**< The address of the last storage location used. */
	uint16_t nv_state;			/**< The current non-volatile state. */
	uint16_t last_nv_stored;	/**< The last state value stored in flash. */
	uint8_t volatile_state;		/**< The current volatile state. */
	platform_mutex state_lock;	/**< Synchronization lock for state. */
	platform_mutex store_lock;	/**< Synchronization lock for store actions. */

	/**
	 * Save the setting for the manifest region that contains the active manifest.
	 * This setting will be stored in non-volatile memory on the next call to store state.
	 *
	 * @param manager The state manager to update.
	 * @param manifest_index Index of manifest to update.
	 * @param active The manifest region to save as the active region.
	 *
	 * @return 0 if the setting was saved or an an error code if the setting was invalid.
	 */
	int (*save_active_manifest) (struct state_manager *manager, uint8_t manifest_index,
		enum manifest_region active);

	/**
	 * Get the current setting for the active manifest region.
	 *
	 * @param manager The state manager to query.
	 * @param manifest_index Index of manifest to retrieve.
	 *
	 * @return The active manifest region.
	 */
	enum manifest_region (*get_active_manifest) (struct state_manager *manager,
		uint8_t manifest_index);

	/**
	 * Restore all state information to the default values.  This does not erase non-volatile state,
	 * but that state will be saved to represent values as if it was erased.
	 *
	 * @param manager The state manager to update.
	 *
	 * @return 0 if the defaults were restored or an error code if the instance is not valid.
	 */
	int (*restore_default_state) (struct state_manager *manager);

	/**
	 * Check whether state manager manifest index provided is valid.
	 *
	 * @param manager The state manager to query.
	 * @param manifest_index Index of manifest to check.
	 *
	 * @return 0 if valid or an error code.
	 */
	int (*is_manifest_valid) (struct state_manager *manager, uint8_t manifest_index);
};


int state_manager_init (struct state_manager *manager, struct flash *state_flash,
	uint32_t store_addr);
void state_manager_release (struct state_manager *manager);

int state_manager_store_non_volatile_state (struct state_manager *manager);
void state_manager_block_non_volatile_state_storage (struct state_manager *manager, bool block);

/* Internal functions for use by derived types. */
int state_manager_save_active_manifest (struct state_manager *manager, enum manifest_region active,
	uint8_t bit);
enum manifest_region state_manager_get_active_manifest (struct state_manager *manager, uint8_t bit);


#define	STATE_MANAGER_ERROR(code)		ROT_ERROR (ROT_MODULE_STATE_MANAGER, code)

/**
 * Error codes that can be generated by a state manager.
 */
enum {
	STATE_MANAGER_INVALID_ARGUMENT = STATE_MANAGER_ERROR (0x00),	/**< Input parameter is null or not valid. */
	STATE_MANAGER_NO_MEMORY = STATE_MANAGER_ERROR (0x01),			/**< Memory allocation failed. */
	STATE_MANAGER_NOT_SECTOR_ALIGNED = STATE_MANAGER_ERROR (0x02),	/**< The base address is not properly aligned. */
	STATE_MANAGER_NOT_BLANK = STATE_MANAGER_ERROR (0x03),			/**< The next flash block was not blank for saving state. */
	STATE_MANAGER_OUT_OF_RANGE = STATE_MANAGER_ERROR (0x04),		/**< Argument is not within the valid range. */
	STATE_MANAGER_INCOMPLETE_WRITE = STATE_MANAGER_ERROR (0x05),	/**< The state was not completely written to flash. */
};


#endif /* STATE_MANAGER_H_ */
